package top.fangwenmagician.calculationstring;

import top.fangwenmagician.calculationstring.exception.FactoryException;
import top.fangwenmagician.calculationstring.operator.*;

import java.util.LinkedList;
import java.util.List;

/**
 * <h1>字符串计算对象工厂</h1>
 * 该工厂用于配置并生成{@link CalculationString}对象。<br />
 * 请在程序开始时就完成对{@link CalculationString}对象的创建，而不是每计算一次创建一次。<br /><br />
 * <h2>创建{@link CalculationString}对象</h2>
 * 通过{@link CalculationFactory#addOperator(I_Operator)}函数添加运算符。<br />
 * {@link CalculationFactory#setConfigure(Object)}函数添加个性化配置，推荐使用线程安全的对象。<br />
 * 使用{@link CalculationFactory#builder()}函数生成{@link CalculationString}对象。<br /><br />
 * <h2>获取和更改配置对象</h2>
 * {@link CalculationFactory}实现了{@link I_CalculationConfigure}接口。<br />
 * 可以通过{@link CalculationFactory#getConfigure()}或{@link CalculationFactory#getConfigure(Class)}来获取配置。<br />
 * 通过{@link CalculationFactory#setConfigure(Object)}来添加个性化配置，推荐使用线程安全的对象。<br /><br />
 * <h2>线程安全性</h2>
 * <b>线程不安全</b><br />
 * 请不要多线程调用该对象。
 */
public class CalculationFactory implements I_CalculationConfigure {
    /**
     * 用于存储添加的运算符
     */
    private final List<I_Operator> operatorList;
    /**
     * 用于存储自定义配置
     */
    private Object configure;

    /**
     * 创建字符串计算工厂函数
     */
    public CalculationFactory() {
        operatorList = new LinkedList<>();
    }

    /**
     * 添加运算符
     *
     * @param operator 运算符对象，需要继承{@link I_Operator}接口
     * @param <T>      需要继承{@link I_Operator}接口
     * @return 返回自己
     */
    public <T extends I_Operator> CalculationFactory addOperator(T operator) {
        operatorList.add(operator);
        return this;
    }

    /**
     * 清空挂载的的运算符
     */
    public void empty() {
        operatorList.clear();
    }

    /**
     * 添加默认运算符（“+”，“-”，“*”，“/”，“（）”）
     */
    public void addDefaultOperator() {
        operatorList.add(new Add());
        operatorList.add(new Subtract());
        operatorList.add(new Multiply());
        operatorList.add(new Divide());
        operatorList.add(new Brackets());
    }

    /**
     * 创建{@link CalculationString}对象
     *
     * @return {@link CalculationString}对象
     * @throws Exception 创建过程中可能出现的错误
     */
    public CalculationString builder() throws Exception {
        if (operatorList.size() == 0) {
            throw new FactoryException("未添加运算符！");
        }
        LinkedList<OperatorGroup> operatorGroupList = new LinkedList<>();
        CalculationString calculationString = new CalculationString(operatorGroupList, configure);

        for (I_Operator operator : operatorList) {
            if (operatorGroupList.size() == 0) {
                OperatorGroup operatorGroup = newOperatorGroup(calculationString, operator);
                operatorGroupList.add(operatorGroup);
            } else {
                for (int i = 0; i < operatorGroupList.size(); i++) {
                    OperatorGroup lOperatorGroup = operatorGroupList.get(i);
                    if (operator.priority() > lOperatorGroup.getPriority()) {
                        // 如果 运算符优先级 大于 当前运算符组的优先级，则新建运算符组并插入
                        OperatorGroup operatorGroup = newOperatorGroup(calculationString, operator);
                        operatorGroupList.add(i, operatorGroup);
                        break;
                    } else if (operator.priority() == lOperatorGroup.getPriority()) {
                        // 如果 运算符优先级 等于 当前运算符组的优先级，则在当前运算符组中插入运算符
                        lOperatorGroup.addOperator(operator);
                        break;
                    } else {
                        // 如果 运算符优先级 小于 当前运算符组的优先级，则检查是否是operatorGroupList的最后一个元素，如果是则在operatorGroupList末尾新建运算符组并插入
                        if (i == operatorGroupList.size() - 1) {
                            OperatorGroup operatorGroup = newOperatorGroup(calculationString, operator);
                            operatorGroupList.add(operatorGroup);
                        }
                    }
                }
            }
        }

        return calculationString;
    }

    /**
     * 内部函数，用于创建新的运算符组
     *
     * @param calculationString {@link CalculationString}对象
     * @param operator          运算符
     * @return 运算符组
     */
    private OperatorGroup newOperatorGroup(CalculationString calculationString, I_Operator operator) throws Exception {
        OperatorGroup operatorGroup = new OperatorGroup(calculationString, operator.priority());
        operatorGroup.addOperator(operator);
        return operatorGroup;
    }

    /**
     * 创建默认{@link CalculationString}对象
     *
     * @return 默认{@link CalculationString}对象
     * @throws Exception 创建过程中可能出现的错误
     */
    public CalculationString builderDefault() throws Exception {
        addDefaultOperator();
        return builder();
    }

    /**
     * 调用{@link CalculationFactory#builderDefault()}创建默认{@link CalculationString}对象
     *
     * @return 默认 {@link CalculationString} 对象
     * @throws Exception 创建过程中可能出现的错误
     */
    public static CalculationString newDefaultCalculationString() throws Exception {
        return new CalculationFactory().builderDefault();
    }

    @Override
    public Object getConfigure() {
        return configure;
    }

    @Override
    public <T> T getConfigure(Class<T> tClass) {
        return tClass.cast(configure);
    }

    public void setConfigure(Object configure) {
        this.configure = configure;
    }
}
